package cn.jhz.learn.community_dynamic.security.manager;

import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * 基于Spring环境运行依赖Spring Cache
 * 生产JWT
 * 缓存JWT
 * 校验JWT
 * 删除JWT
 */
@Service
public class JwtManager {

    public RsaJsonWebKey newRsaJsonWebKey(String keyId) throws JoseException {
        RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
        rsaJsonWebKey.setKeyId(keyId);
        return rsaJsonWebKey;
    }

    @Cacheable(cacheNames = "rsaCache", key = "#keyId", unless = "#result == null")
    public Optional<RsaJsonWebKey> rsaJsonWebKey(RsaJsonWebKey rsaJsonWebKey, String keyId) {
        return Optional.ofNullable(rsaJsonWebKey);
    }

    @CacheEvict(cacheNames="rsaCache", allEntries=true)
    public void clearRsaJsonWebKey(String keyId){}

    

    public String newInstance(String subject, RsaJsonWebKey rsaJsonWebKey){
        return newInstance(subject, "system", "user", rsaJsonWebKey);
    }

    /**
     * JSON Web Token是一种紧凑的url安全的方式，用于表示在双方之间传输的声明/属性
     *
     * JWT是一个JWS和/或一个JWE, JSON声明为有效负载
     *
     * 对JWS签名并生成紧凑的序列化或完整的JWT/JWS表示，
     * 这是一个字符串，由Header.Payload表单中三个分隔的base64url编码部分组成。
     * 如果希望对签名进行加密，只需将这个jwt设置为JsonWebEncryption对象的有效负载，
     * 并将cty(内容类型)头设置为“jwt”。
     * @throws JoseException
     * @throws MalformedClaimException
     */
    public String newInstance(String subject, String issuer, String audience, RsaJsonWebKey rsaJsonWebKey){
        // 创建声明，它将是JWT的内容
        JwtClaims claims = new JwtClaims();
        claims.setIssuer(issuer);  // 令牌创建者的签名
        claims.setAudience(audience); // 令牌接收者的签名
        claims.setExpirationTimeMinutesInTheFuture(1000); // 令牌到期时间(min)10
        claims.setGeneratedJwtId(); // 令牌的唯一标识符
        claims.setIssuedAtToNow();  // 令牌发出/创建的时间(now)
        //claims.setNotBeforeMinutesInThePast(1); // 令牌开始生效时间(min)2
        claims.setSubject(subject); // 主题/主体是这个token的内容

//        claims.setClaim("email","mail@example.com"); // 可以添加关于主题的其他声明/属性
//        List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
//        claims.setStringListClaim("groups", groups); // 多值声明也可以，并将以JSON数组的形式结束

        JsonWebSignature jws = new JsonWebSignature();
        // JWS的有效负载是JWT声明的JSON内容
        jws.setPayload(claims.toJson());
        // JWT是使用私钥签名的
        jws.setKey(rsaJsonWebKey.getPrivateKey());
        // 设置键ID (kid)头，因为这只是优雅的写法。使用一个键ID有助于平滑的键翻转过程
        jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
        // 设置签名JWT/jws完整性保护的算法
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

        try {
            return jws.getCompactSerialization();
        } catch (JoseException e) {
            throw new RuntimeException("jwt创建异常", e);
        }
    }

    public JwtClaims validate(String jwt, RsaJsonWebKey rsaJsonWebKey) throws InvalidJwtException {
        return validate(jwt, "system", "user", rsaJsonWebKey);
    }
    
    public JwtClaims validate(String jwt, String issuer, String audience, RsaJsonWebKey rsaJsonWebKey) throws InvalidJwtException {
        // 使用JwtConsumerBuilder构造适当的JwtConsumer，它将用于验证和处理JWT。
        // JWT的特定验证要求取决于上下文，但是，通常建议要求(合理的)过期时间、可信的发布方和将系统标识为目标接收方的受众。
        // 如果JWT也是加密的，则只需向构建器提供解密密钥或解密密钥解析器。
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime() // JWT必须有一个过期时间
                .setAllowedClockSkewInSeconds(30) // 在验证基于时间的声明以解释时钟偏移方面有一些余地
                .setRequireSubject() // JWT必须有一个主题
                .setExpectedIssuer(issuer) // JWT需要由谁签发
                .setExpectedAudience(audience) // JWT是为谁准备的
                .setVerificationKey(rsaJsonWebKey.getKey()) // 使用公钥验证签名
                .setJwsAlgorithmConstraints( // 只允许预期的签名algorithm(s)给定的上下文
                	ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256) // 这里是RS256
                .build(); // 创建JwtConsumer实例
        
        //  验证JWT并处理它的声明
        return jwtConsumer.processToClaims(jwt);
    }
    
//    public JwtClaims test(String jwt, RsaJsonWebKey rsaJsonWebKey) throws InvalidJwtException, JoseException {
//
//	    // Create a new JsonWebSignature
//	    JsonWebSignature jws = new JsonWebSignature();
//
//	    // Set the algorithm constraints based on what is agreed upon or expected from the sender
//	    jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256));
//
//	    // Set the compact serialization on the JWS
//	    jws.setCompactSerialization(jwt);
//
//	    jws.setKey(rsaJsonWebKey.getPublicKey());
//
//	    // Check the signature
//	    System.out.println(jws.verifySignature());
//	    
//	 // 创建声明，它将是JWT的内容
//	        JwtClaims claims = new JwtClaims();
//	        claims.setIssuer("system");  // 令牌创建者的签名
//	        claims.setAudience("user"); // 令牌接收者的签名
//	        claims.setExpirationTimeMinutesInTheFuture(1000); // 令牌到期时间(min)10
//	        claims.setGeneratedJwtId(); // 令牌的唯一标识符
//	        claims.setIssuedAtToNow();  // 令牌发出/创建的时间(now)
//	        //claims.setNotBeforeMinutesInThePast(1); // 令牌开始生效时间(min)2
//	        try {
//		    claims.setSubject((String) mapper.readValue(jws.getPayload(), Map.class).get("sub"));
//		} catch (JsonMappingException e) {
//		    // TODO Auto-generated catch block
//		    e.printStackTrace();
//		} catch (JsonProcessingException e) {
//		    // TODO Auto-generated catch block
//		    e.printStackTrace();
//		} catch (JoseException e) {
//		    // TODO Auto-generated catch block
//		    e.printStackTrace();
//		} // 主题/主体是这个token的内容
//	        
//	    
//	    return claims;
//  }

}
